package com.bungaurs.sdk;

import com.bungarus.codec.protobuf.MessageProtobuf;
import com.bungarus.sdk.SmarterClient;
import com.bungarus.sdk.listener.ClientMessageListener;
import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * Created by tang on 2019/2/4.
 */
public class StressTestClient extends SmarterClient {
    private static final String SLEEP_TIME = "sleep.time";
    static Logger logger = Logger.getLogger(StressTestClient.class);

    private static final String MEMBER_START_STR = "member.start";
    private static final String MEMBER_END_STR = "member.end";

    private final static String DEFALUT_HOST_STR = "access.host";
    private final static String ACCESS_PORT_STR = "access.port";
    private final static String DEFAULT_ACCESS_PORT_VALUE = "80";

    private String userName;
    private String password;
    private String userAccount;

    public static void main(String[] args) throws Exception {
        int idx_member_start;
        int idx_member_end;
        String start = System.getProperty(MEMBER_START_STR);
        if(null == start) {
            throw new IllegalArgumentException("member.start required.");
        }
        String end = System.getProperty(MEMBER_END_STR);
        if(null == end) {
            throw new IllegalArgumentException("member.end required.");
        }
        int sleepTime = Integer.valueOf(System.getProperty(SLEEP_TIME, "500"));
        int accessPort = Integer.valueOf(System.getProperty(ACCESS_PORT_STR, DEFAULT_ACCESS_PORT_VALUE));
        String accessHost = System.getProperty(DEFALUT_HOST_STR,"");

        idx_member_start = Integer.valueOf(start);
        idx_member_end = Integer.valueOf(end);

        if(idx_member_start < 0 || idx_member_end < idx_member_start) {
            throw new IllegalArgumentException("error input value");
        }
        logger.debug("Start stress testing...");
        logger.debug("Start to login test...\n");
        InetSocketAddress serverAddress = new InetSocketAddress(accessHost, accessPort);
        List<SmarterClient> stressClients = new LinkedList<>();
        CountDownLatch latch = new CountDownLatch(idx_member_end - idx_member_start + 1);
        for(int i=idx_member_start; i<=idx_member_end; i++) {
            StressTestClient client = new StressTestClient();
            client.setUserName("username" + (i==0?"":"" + (i + 1)));
            client.setPassword("password" + (i + 1));
            client.setUserAccount("" + (1001 + i));
            client.addListener(new StressProcessingListener());

            client.connect(serverAddress, value -> {
                MessageProtobuf.Message loginMsg = client.makeloginMessage();
                client.send(loginMsg, result -> {
                    stressClients.add(client);
                    latch.countDown();
                    return null;
                });
                return null;
            });
        }

        latch.await();
        while (true) {
            logger.debug("A new loop begins...");
            stressClients.forEach(client -> {
                int range = idx_member_end - idx_member_start + 1;
                int idx = (int) Math.round(Math.random() * range);
                int index = idx > range ? range : idx;
                if (null == client.getLoggedIn()) {
                    return;
                }
                MessageProtobuf.Message msg = TestClientMessageBuilder.buildIMMessage(client.getLoggedIn(),
                        String.valueOf(index + 1001), "Apo", "Device", "Stress test from " + client.getLoggedIn().getUserId());
                client.send(msg, value -> {
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.append("Source: ");
                    stringBuilder.append(msg.getHeader().getFrom());
                    stringBuilder.append(" -> ");
                    stringBuilder.append(msg.getHeader().getTo());
                    stringBuilder.append(" sent message at ");
                    stringBuilder.append(msg.getHeader().getSentTime());
                    stringBuilder.append(" milliseconds. ");
                    logger.info(stringBuilder.toString());
                    stringBuilder.setLength(0);

                    return null;
                });
            });
            Thread.sleep(sleepTime);
            logger.info("\n");
        }
    }

    public static String getInputString(){
        String line = "";
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            line = reader.readLine();
        }catch(IOException e){
            e.printStackTrace();
        }
        return line;
    }

    public StressTestClient() {
        prepare();
    }

    private void prepare() {
    }

    @Override
    public MessageProtobuf.Message makeloginMessage() {
        MessageProtobuf.Message message = TestClientMessageBuilder.buildLoginMessage(
                getUserAccount(),
                10000,
                "Apo",
                "Device",
                getUserName(),
                getPassword()
        );
        return message;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPassword() {
        return password;
    }

    public String getUserName() {
        return userName;
    }

    public String getUserAccount() {
        return userAccount;
    }

    public void setUserAccount(String userAccount) {
        this.userAccount = userAccount;
    }

    private static class StressProcessingListener implements ClientMessageListener {
        static Logger logger = Logger.getLogger(StressProcessingListener.class);
        StringBuilder sb = new StringBuilder();
        @Override
        public void onStatus(MessageProtobuf.Message status) {
//            logger.debug("BusiProcessingListener onStatus \n");
        }

        @Override
        public void onIM(MessageProtobuf.Message chatMessage) {
            sb.append("Dest: ");
            sb.append(chatMessage.getHeader().getFrom());
            sb.append(" -> ");
            sb.append(chatMessage.getHeader().getTo());
            sb.append(" takes ");
            sb.append((System.currentTimeMillis() - chatMessage.getHeader().getSentTime()));
            sb.append(" milliseconds. ");
            logger.info(sb.toString());
            sb.setLength(0);
        }

        @Override
        public void onGroupIM(MessageProtobuf.Message groupMessage) {
            logger.debug("BusiProcessingListener onGroupIM \n");
        }

        @Override
        public void onOthers(MessageProtobuf.Message otherMessage) {
//            logger.debug("BusiProcessingListener onOthers \n");
        }

        @Override
        public void onAuthExpire() {

        }

        @Override
        public void onUserNotExist() {

        }
    }
}
